﻿using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Cpuss.Strategies.Tests
{

    /// <summary>
    /// Tests for SJF strategy.
    /// </summary>
    [TestFixture]
    public class ShortestJobFirstTest
    {

        /// <summary>
        /// Check to see that the SJF algorithm collects the correct metrics when a process
        /// queue with no idle CPU time is used.
        /// </summary>
        [Test]
        public void SjfNoIdleCpuTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 26, 0),
                new Process(2, 18, 4),
                new Process(3, 7, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 26, 0)
            {
                StartTime = 0,
                CompletionTime = 51
            });
            expected.Add(2, new Process(2, 18, 4)
            {
                StartTime = 9,
                CompletionTime = 27
            });
            expected.Add(3, new Process(3, 7, 2)
            {
                StartTime = 2,
                CompletionTime = 9
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the SJF algorithm collects the correct metrics when a process
        /// queue with idle CPU time is used.
        /// </summary>
        [Test]
        public void SjfSomeIdleCpuTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 4),
                new Process(2, 21, 16),
                new Process(3, 8, 12),
                new Process(4, 3, 31),
                new Process(5, 39, 9)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 8, 4)
            {
                StartTime = 4,
                CompletionTime = 12
            });
            expected.Add(2, new Process(2, 21, 16)
            {
                StartTime = 20,
                CompletionTime = 44
            });
            expected.Add(3, new Process(3, 8, 12)
            {
                StartTime = 12,
                CompletionTime = 20
            });
            expected.Add(4, new Process(4, 3, 31)
            {
                StartTime = 31,
                CompletionTime = 34
            });
            expected.Add(5, new Process(5, 39, 9)
            {
                StartTime = 44,
                CompletionTime = 83
            });

            actual.Run();

            for (int i = 1; i <= actual.Metrics.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct string is returned.
        /// </summary>
        [Test]
        public void ToStringTest()
        {
            ShortestJobFirst actual = new ShortestJobFirst(1);

            Assert.AreEqual("Shortest Job First (Poll Time = 1)", actual.ToString());
        }

        /// <summary>
        /// Check to see that the correct process results are logged when defining a poll time.
        /// </summary>
        [Test]
        public void PollTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 0),
                new Process(2, 4, 4),
                new Process(3, 3, 1),
                new Process(4, 7, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(2));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 2, 0)
            {
                StartTime = 0,
                CompletionTime = 2
            });
            expected.Add(2, new Process(2, 4, 4)
            {
                StartTime = 5,
                CompletionTime = 9
            });
            expected.Add(3, new Process(3, 3, 1)
            {
                StartTime = 2,
                CompletionTime = 5
            });
            expected.Add(4, new Process(4, 7, 2)
            {
                StartTime = 9,
                CompletionTime = 16
            });

            actual.Run();

            for (int i = 1; i <= actual.Metrics.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the poll time is greater than 0, othwerise throw the correct exception.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void PollTimeLessThanOneTest()
        {
            ShortestJobFirst actual = new ShortestJobFirst(0);
        }

    }
}
